/*  GStruct: data structure helpers for Java
 *  Copyright (C) 2011 Giulio Franco
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package gstruct.sequential.lazy;

import java.util.AbstractCollection;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;

/**
 * Collection implementation that lazily concatenates
 * multiple backing collections.
 * 
 * Since this collection is lazy,
 * iteration may fail if the backing collections are modified
 * after the iteration has started.
 * Also, it inherits thread-safety properties
 * from its backing collections.
 * @author Giulio Franco <giulio_franco@msn.com>
 *
 * @param <E>	Type of the contained objects
 */
public class ConcatenateCollection<E> extends AbstractCollection<E> {

	private class ConcatenateIterator implements Iterator<E> {
		private Iterator<? extends E> currentIterator;
		private boolean lastCollection;
		private int nextCollection;
		
		private ConcatenateIterator() {
			currentIterator = colls[0].iterator();
			lastCollection = (colls.length == 0);
			nextCollection = 1;
		}
		
		@Override
		public boolean hasNext() {
			if (currentIterator.hasNext()) {
				return true;
			} else if (lastCollection) {
				return false;
			} else {
				switchOff();
				return hasNext();
			}
		}
		
		private void switchOff() {
			currentIterator = colls[nextCollection++].iterator();
			lastCollection = (nextCollection == colls.length);
		}
		
		@Override
		public E next() {
			if (!(currentIterator.hasNext() || lastCollection)) {
				switchOff();
			}
			
			return currentIterator.next();
		}
		
		@Override
		public void remove() {
			throw new UnsupportedOperationException();
		}
	}
	
	private final Collection<? extends E>[] colls;
	
	@SuppressWarnings("unchecked")
	public ConcatenateCollection() {
		colls = new Collection[] {Collections.emptyList()};
	}
	
	@SafeVarargs
	public ConcatenateCollection(Collection<? extends E>... colls) {
		this.colls = Arrays.copyOf(colls, colls.length);
	}
	
	@Override
	public Iterator<E> iterator() {
		return new ConcatenateIterator();
	}

	@Override
	public int size() {
		int sz = 0;
		for (Collection<? extends E> c : colls) {
			sz += c.size();
		}
		return sz;
	}

}
